#pragma once

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
#include <functional>
#include <signal.h>
#include <pthread.h>
#include "err.hpp"
#include "Thread.hpp"
#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"

namespace ns_server{
    using namespace std;
    static const uint16_t defaultPort = 8086;
    static const int backlog = 32;

    using func_t = function<string(const string&)>;

    class TcpServer;
    class ThreadData{
        public:
            ThreadData(int fd, const string &ip, const uint16_t& port, TcpServer* ts)
            :sock(fd), clientIp(ip), clientPort(port), current(ts){

            }

            int sock;
            string clientIp;
            uint16_t clientPort;
            TcpServer *current;
    };

    class TcpServer{
        public:
            TcpServer(func_t func, uint16_t port = defaultPort) : func_(func), port_(port), quit_(true){

            }

            void initServer(){
                //1.创建socket, 文件
                listenSock_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listenSock_ < 0){
                    logMessage(Fatal, "create socket error, code: %d, error string: %s", errno, strerror(errno));
                    exit(SOCKET_ERR);
                }
                logMessage(Info, "create socket success, code: %d, error string: %s", errno, strerror(errno));

                //2.bind
                struct sockaddr_in local;
                memset(&local, 0, sizeof(local));
                local.sin_family = AF_INET;
                local.sin_port = htons(port_);
                local.sin_addr.s_addr = htonl(INADDR_ANY);
                if(bind(listenSock_, (struct sockaddr*)&local, sizeof(local)) < 0){
                    logMessage(Fatal, "bind socket error, code: %d, error string: %s", errno, strerror(errno));
                    exit(BIND_ERR);
                }
                logMessage(Info, "bind socket success, code: %d, error string: %s", errno, strerror(errno));

                //3.监听
                if(listen(listenSock_, backlog) < 0){
                    logMessage(Fatal, "listen socket error, code: %d, error string: %s", errno, strerror(errno));
                    exit(LISTEN_ERR);
                }
                logMessage(Info, "listen socket success, code: %d, error string: %s", errno, strerror(errno));
            }

            void start(){

                signal(SIGCHLD, SIG_IGN);

                quit_ = false;
                while(!quit_){
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);

                    //4.获取连接, accept
                    int sock = accept(listenSock_, (struct sockaddr*)&client, &len);
                    if(sock < 0){
                        cerr << "accept error" << endl;
                        logMessage(Warning, "accept error, code: %d, error string: %s", errno, strerror(errno));
                        continue;
                    }

                    //提取client信息
                    string clientIp = inet_ntoa(client.sin_addr);
                    uint16_t clientPort = ntohs(client.sin_port);

                    //获取连接成功，开始业务处理
                    logMessage(Info, "connect success: %d from %d, who: %s - %d",sock, listenSock_, clientIp.c_str(), clientPort);

                    //线程池
                    //注意: 线程有限，所以要处理短任务
                    Task t(sock, clientIp, clientPort, bind(&TcpServer::service, this, placeholders::_1, placeholders::_2, placeholders::_3));
                    ThreadPool<Task>::getInstance()->pushTask(t);
                }
            }

            static void* threadRoutine(void* args){
                //线程分离，不需要回收
                pthread_detach(pthread_self());

                ThreadData* td = static_cast<ThreadData*>(args);
                td->current->service(td->sock, td->clientIp, td->clientPort);
                delete td;
                return nullptr;
            }

            void service(int sock, const string& clientIp, const uint16_t& clientPort){
                string who = clientIp + "-" + to_string(clientPort);
                char buffer[1024];
                ssize_t s = read(sock, buffer, sizeof(buffer)-1);
                if(s > 0){
                    buffer[s] = 0;
                    string res = func_(buffer);
                    logMessage(Debug, "%s# %s", who.c_str(), res.c_str());
                    write(sock, res.c_str(), res.size());
                }
                else if(s == 0){
                    logMessage(Info, "%s quit,me too", who.c_str());
                }
                else{
                    logMessage(Error, "read error, %d:%s", errno, strerror(errno));
                }
                close(sock);
             }

            ~TcpServer(){

            }

        private:
            uint16_t port_;
            int listenSock_;
            bool quit_;
            func_t func_;
    };
}